package top.zhangjianyong.tools.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import top.zhangjianyong.tools.entity.ETF;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.model.ETFMedianInfo;
import top.zhangjianyong.tools.model.ETFOpportunity;
import top.zhangjianyong.tools.service.mybatis.IETFDataService;
import top.zhangjianyong.tools.service.mybatis.IETFService;
import top.zhangjianyong.tools.utils.SoHuETFUtil;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.stream.Collectors;
import static java.time.temporal.ChronoUnit.DAYS;

/**
 * @author ： zhangjianyong
 * @date ： 2022/11/21 2:53 PM
 */
@Slf4j
@Component
public class ETFMedianUtil {

    private final IETFDataService ietfDataService;
    private final IETFService ietfService;
    private final HolidayUtil holidayUtil;

    public ETFMedianUtil(IETFDataService ietfDataService, IETFService ietfService, HolidayUtil holidayUtil) {
        this.ietfDataService = ietfDataService;
        this.ietfService = ietfService;
        this.holidayUtil = holidayUtil;
    }

    //计算一个月内的涨跌幅回撤
    public ETFMedianInfo etfMedianInfo(ETF etf) {
        return etfMedianInfo(etf, 1, ChronoUnit.MONTHS);
    }

    public List<ETFMedianInfo> etfMedianInfo(List<ETF> list, ChronoUnit unit, int num) {

        return list.stream()
            .map(etf -> etfMedianInfo(etf, num, unit))
            .sorted(Comparator.comparing(ETFMedianInfo::getHcf))
            .collect(Collectors.toList());
    }

    /**
     * 对ETF历史数据进行线性回归分析，并直接设置相关结果到ETFMedianInfo对象。
     * 包括斜率、R2、预测价格、预测涨跌幅。
     *
     * @param list          ETF历史数据列表（时间顺序，通常为早到晚）
     * @param currentValue  当前ETF的最新净值
     * @param etfMedianInfo 结果对象，设置斜率、R2、预测等
     */
    private void calculateLinearRegressionResult(List<ETFData> list, BigDecimal currentValue, ETFMedianInfo etfMedianInfo) {
        LocalDate vd1 = list.get(0).getValueDate();
        LocalDate ld = list.get(list.size() - 1).getValueDate();
        double[] x = new double[list.size()];
        double[] y = new double[list.size()];
        for (int i = 0; i < list.size(); i++) {
            ETFData data = list.get(i);
            LocalDate vd2 = data.getValueDate();
            x[i] = DAYS.between(vd1, vd2);
            y[i] = data.getValue().doubleValue();
        }
        LinearRegression linearRegression = new LinearRegression(x, y);
        BigDecimal slope = BigDecimal.valueOf(linearRegression.slope())
            .multiply(new BigDecimal(100))
            .setScale(2, RoundingMode.HALF_DOWN);
        BigDecimal r2 = BigDecimal.valueOf(linearRegression.R2())
            .setScale(2, RoundingMode.HALF_DOWN);
        LocalDate nextTradeDate = this.holidayUtil.nextStock(ld);
        long nextDay = DAYS.between(vd1, nextTradeDate);
        BigDecimal predict = BigDecimal.valueOf(linearRegression.predict(nextDay)).setScale(3, RoundingMode.HALF_DOWN);
        BigDecimal predictRate = predict.subtract(currentValue)
            .divide(currentValue, 4, RoundingMode.HALF_UP)
            .multiply(new BigDecimal(100));
        // 置信度过滤：只有R2大于0.7时才设置，否则设为null
        if (r2.compareTo(new BigDecimal("0.7")) > 0) {
            etfMedianInfo.setSlope(slope);
            etfMedianInfo.setR2(r2);
            etfMedianInfo.setPredict(predict);
            etfMedianInfo.setPredictRate(predictRate);
        } else {
            etfMedianInfo.setSlope(null);
            etfMedianInfo.setR2(r2); // 仍然显示R2，便于前端判断
            etfMedianInfo.setPredict(null);
            etfMedianInfo.setPredictRate(null);
        }
    }

    /**
     * 计算指定ETF在给定时间范围内的多项统计指标（如涨跌幅、最大/最小/区间、线性回归、RSI、预测等）。
     *
     * @param etf         需要分析的ETF对象
     * @param amountToAdd 时间跨度（如10，表示10天/10周/10月等，取决于unit）
     * @param unit        时间单位（ChronoUnit.DAYS/WEEKS/MONTHS/YEARS等）
     * @return ETFMedianInfo 统计分析结果对象，包含多项指标
     */
    public ETFMedianInfo etfMedianInfo(ETF etf, long amountToAdd, TemporalUnit unit) {
        // 1. 计算实际天数（如1月=20天，1年=250天等）
        long days = getDays(amountToAdd, unit);
        String code = etf.getCode();

        // 2. 查询该ETF最近days天的历史数据，并反转顺序（时间从早到晚）
        List<ETFData> list = ietfDataService.listLastLimit(code, days);
        Collections.reverse(list);

        // 3. 清理异常数据（如拆分合并等特殊节点）
        list = clearList(list);

        // 4. 设定基础值（100），用于后续涨跌幅等计算
        BigDecimal base = new BigDecimal(100);

        // 5. 获取当前最新的ETF数据
        ETFData ed = list.get(list.size() - 1);
        LocalDate lastDate = ed.getValueDate();
        // 获取list中的开始日期和结束日期
        LocalDate startDate = list.get(0).getValueDate();
        LocalDate endDate = list.get(list.size() - 1).getValueDate();

        // 6. 如果历史数据日期早于ETF对象日期，则补充最新数据
        if (lastDate.isBefore(etf.getValueDate().toLocalDate())) {
            // 先尝试从数据库查询当日的ETFData（包含volume数据）
            LocalDate todayDate = etf.getValueDate().toLocalDate();
            ETFData todayData = ietfDataService.queryOne(code, todayDate);
            if (todayData != null) {
                // 使用数据库中的当日数据（包含volume）
                list.add(todayData);
            } else {
                // 如果数据库中没有当日数据，尝试从SoHuETFUtil获取包含volume的数据
                ETFData latestData = createETFDataWithVolume(etf);
                list.add(latestData);
            }
        }
        ETFData etfData = list.get(list.size() - 1);

        // 7. 计算辅助累计净值（用于后续涨跌幅、最大/最小/区间等指标）
        setAveValue(list, base);

        // 8. 统计最大、最小、区间
        BigDecimal max = list.stream().map(ETFData::getAvgValue).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        BigDecimal min = list.stream().map(ETFData::getAvgValue).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        BigDecimal rang = max.subtract(min);

        // 9. 构建结果对象，填充基础信息
        ETFMedianInfo etfMedianInfo = new ETFMedianInfo();
        etfMedianInfo.setRang(rang.setScale(2, RoundingMode.HALF_UP));
        etfMedianInfo.setDateTime(etf.getValueDate());
        etfMedianInfo.setValue(etfData.getValue());
        etfMedianInfo.setDailyRate(etfData.getDailyRate());
        etfMedianInfo.setCode(code);
        etfMedianInfo.setName(etfData.getName());
        etfMedianInfo.setType("一个月内");
        etfMedianInfo.setStartDate(startDate);
        etfMedianInfo.setEndDate(endDate);

        // 10. 计算涨跌幅（当前累计净值与base的比值）
        BigDecimal zdf = etfData.getAvgValue().subtract(base).divide(base, 4, RoundingMode.HALF_UP);
        etfMedianInfo.setZdf(zdf.multiply(new BigDecimal(100)));

        // 11. 计算上升幅（当前累计净值与最小值的比值）
        BigDecimal ssf = etfData.getAvgValue().subtract(min).divide(min, 4, RoundingMode.HALF_UP);
        etfMedianInfo.setSsf(ssf.multiply(new BigDecimal(100)));

        // 12. 计算当前净值在区间中的位置（0-100%）
        BigDecimal position = new BigDecimal("9.99");
        if (rang.compareTo(BigDecimal.ZERO) != 0) {
            position = etfData.getAvgValue().subtract(min).divide(rang, 4, RoundingMode.HALF_UP);
        }
        etfMedianInfo.setPosition(position.multiply(new BigDecimal(100)));
        etfMedianInfo.setHoldFlag(etf.getHoldFlag());
        etfMedianInfo.setDays(list.size());

        // 13. 线性回归分析（用于趋势判断和预测），包含预测价格和预测涨跌幅，直接设置到结果对象
        calculateLinearRegressionResult(list, etfData.getValue(), etfMedianInfo);

        // 16. 计算RSI指标（相对强弱指数）
        countRSI(etfMedianInfo, list);

        // 17. 计算最近涨跌幅度
        Collections.reverse(list);

        // 18. 计算最大回撤
        etfMedianInfo.setHcf(getHcz(list));

        // 19. 计算连续涨跌幅
        double rate = getRate(list);
        etfMedianInfo.setLastUpDown(new BigDecimal(rate).setScale(2, RoundingMode.HALF_UP));

        // 20. 计算当日量比
        calculateVolumeRatio(etfMedianInfo, list, etfData);

        return etfMedianInfo;
    }

    /**
     * 获取指定ETF的机会指标信息。
     * <p>
     * 该方法会基于ETF的历史数据，计算回撤（hcz）、线性回归斜率（slope）、
     * 拟合优度（r2）和RSI等指标，并封装为ETFOpportunity对象返回。
     * 这些指标用于后续判断ETF是否存在投资机会。
     *
     * @param etf ETF对象，包含基本信息和最新净值
     * @return ETFOpportunity 机会指标对象，包含hcz、slope、r2、rsi等
     */
    public ETFOpportunity etfOpportunity(ETF etf) {
        String code = etf.getCode();
        // 获取最近60日的ETF历史数据
        List<ETFData> list = ietfDataService.listLastLimit(code, 60);
        ETFData ed = list.get(0);
        LocalDate lastDate = ed.getValueDate();
        // 如果最新数据早于ETF对象的日期，则将ETF对象数据插入到最前面
        if (lastDate.isBefore(etf.getValueDate().toLocalDate())) {
            list.add(0, new ETFData(etf));
        }

        // 计算最近60日的最大回撤
        BigDecimal hcz = getHcz(list.subList(0, 60));
        // 计算最近7日的线性回归斜率和拟合优度
        LinearRegression linearRegression = getHgz(list.subList(0, 7));
        double slope = linearRegression.slope();//斜率
        double r2 = linearRegression.R2(); // 拟合度

        // 计算最近14日的RSI指标
        BigDecimal rsi = countRSI(list.subList(0, 14));
        // 封装结果
        ETFOpportunity opportunity = new ETFOpportunity();
        opportunity.setHcz(hcz);
        opportunity.setSlope(new BigDecimal(slope));
        opportunity.setR2(new BigDecimal(r2));
        opportunity.setRsi(rsi);
        opportunity.setCode(etf.getCode());
        opportunity.setName(etf.getName());
        return opportunity;
    }

    private BigDecimal countRSI(List<ETFData> list) {
        try {
            BigDecimal up = BigDecimal.ZERO;
            int upCount = 0;
            BigDecimal down = BigDecimal.ZERO;
            int downCount = 0;
            for (ETFData etfData : list) {
                BigDecimal dailyRate = etfData.getDailyRate();
                if (dailyRate.compareTo(BigDecimal.ZERO) > 0) {
                    up = up.add(dailyRate);
                    upCount++;
                } else if (dailyRate.compareTo(BigDecimal.ZERO) < 0) {
                    down = down.add(dailyRate);
                    downCount++;
                }
            }
            // 动态阈值：至少2天，或总天数的20%
            int threshold = Math.max(2, list.size() / 4);
            if (upCount < threshold || downCount < threshold) {
                return null;
            }
            up = up.divide(new BigDecimal(upCount), 8, RoundingMode.HALF_UP);
            down = down.divide(new BigDecimal(downCount), 8, RoundingMode.HALF_UP);

            if (down.compareTo(BigDecimal.ZERO) == 0) {
                return new BigDecimal(100);
            }
            BigDecimal rs = up.divide(down.abs(), 8, RoundingMode.HALF_UP);
            BigDecimal rsi = new BigDecimal(100)
                .subtract(new BigDecimal(100)
                    .divide(rs.add(new BigDecimal(1)), 8, RoundingMode.HALF_UP));
            return rsi.setScale(2, RoundingMode.HALF_UP);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 使用Wilder's RSI算法计算相对强弱指数
     * Wilder's RSI使用指数平滑而不是简单平均，让最近的数据有更大的权重
     * 
     * @param list ETF历史数据列表，按时间顺序排列（早到晚）
     * @return RSI值，范围0-100，null表示计算失败
     */
    private BigDecimal countWilderRSI(List<ETFData> list) {
        try {
            // Wilder's RSI需要至少14个数据点
            if (list.size() < 14) {
                log.warn("Wilder's RSI需要至少14个数据点，当前数据点: {}", list.size());
                return null;
            }

            // 确保数据按时间顺序排列（早到晚）
            List<ETFData> sortedList = new ArrayList<>(list); 

            // 计算前14天的初始平均上涨和下跌
            BigDecimal initialAvgGain = BigDecimal.ZERO;
            BigDecimal initialAvgLoss = BigDecimal.ZERO;
            
            for (int i = 0; i < 14; i++) {
                ETFData data = sortedList.get(i);
                BigDecimal dailyRate = data.getDailyRate();
                
                if (dailyRate.compareTo(BigDecimal.ZERO) > 0) {
                    initialAvgGain = initialAvgGain.add(dailyRate);
                } else if (dailyRate.compareTo(BigDecimal.ZERO) < 0) {
                    initialAvgLoss = initialAvgLoss.add(dailyRate.abs());
                }
            }
            
            // 计算初始平均值
            initialAvgGain = initialAvgGain.divide(new BigDecimal(14), 8, RoundingMode.HALF_UP);
            initialAvgLoss = initialAvgLoss.divide(new BigDecimal(14), 8, RoundingMode.HALF_UP);
            
            // 如果初始平均下跌为0，说明前14天都是上涨，RSI为100
            if (initialAvgLoss.compareTo(BigDecimal.ZERO) == 0) {
                return new BigDecimal(100);
            }
            
            // 使用Wilder's平滑公式更新平均值（从第15天开始）
            BigDecimal currentAvgGain = initialAvgGain;
            BigDecimal currentAvgLoss = initialAvgLoss;
            
            // 平滑系数：13/14 (N-1)/N，其中N=14
            BigDecimal smoothFactor = new BigDecimal("13");
            BigDecimal period = new BigDecimal("14");
            
            for (int i = 14; i < sortedList.size(); i++) {
                ETFData data = sortedList.get(i);
                BigDecimal dailyRate = data.getDailyRate();
                
                BigDecimal todayGain = BigDecimal.ZERO;
                BigDecimal todayLoss = BigDecimal.ZERO;
                
                if (dailyRate.compareTo(BigDecimal.ZERO) > 0) {
                    todayGain = dailyRate;
                } else if (dailyRate.compareTo(BigDecimal.ZERO) < 0) {
                    todayLoss = dailyRate.abs();
                }
                
                // Wilder's平滑公式：
                // 新平均值 = (前一日平均值 × (N-1) + 今日值) / N
                currentAvgGain = currentAvgGain.multiply(smoothFactor)
                    .add(todayGain)
                    .divide(period, 8, RoundingMode.HALF_UP);
                    
                currentAvgLoss = currentAvgLoss.multiply(smoothFactor)
                    .add(todayLoss)
                    .divide(period, 8, RoundingMode.HALF_UP);
            }
            
            // 如果当前平均下跌为0，RSI为100
            if (currentAvgLoss.compareTo(BigDecimal.ZERO) == 0) {
                return new BigDecimal(100);
            }
            
            // 计算RS值
            BigDecimal rs = currentAvgGain.divide(currentAvgLoss, 8, RoundingMode.HALF_UP);
            
            // 计算RSI值：RSI = 100 - (100 / (1 + RS))
            BigDecimal rsi = new BigDecimal(100)
                .subtract(new BigDecimal(100)
                    .divide(rs.add(BigDecimal.ONE), 8, RoundingMode.HALF_UP));
            
            return rsi.setScale(2, RoundingMode.HALF_UP);
            
        } catch (Exception e) {
            log.error("计算Wilder's RSI时发生错误: {}", e.getMessage(), e);
        }
        return null;
    }

    private LinearRegression getHgz(List<ETFData> list) {
        Collections.reverse(list);
        LocalDate vd1 = list.get(0).getValueDate();
        double[] x = new double[list.size()];
        double[] y = new double[list.size()];
        LocalDate ld = null;
        for (int i = 0; i < list.size(); i++) {
            ETFData data = list.get(i);
            LocalDate vd2 = data.getValueDate();
            x[i] = DAYS.between(vd1, vd2);
            y[i] = data.getValue().doubleValue();
            ld = data.getValueDate();
        }
        return new LinearRegression(x, y);
    }

    private BigDecimal getHcz(List<ETFData> list) {
        BigDecimal value = list.get(0).getValue();
        //最大值
        BigDecimal max = list.stream().map(ETFData::getValue).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        BigDecimal hcf = value.subtract(max).divide(max, 4, RoundingMode.HALF_UP);
        return hcf.multiply(new BigDecimal(100));
    }

    private long getDays(long amountToAdd, TemporalUnit unit) {
        if (unit instanceof ChronoUnit) {
            switch ((ChronoUnit) unit) {
                case DAYS:
                    return amountToAdd;
                case WEEKS:
                    return amountToAdd * 5;
                case MONTHS:
                    return amountToAdd * 20;
                case YEARS:
                    return amountToAdd * 250;
                default:
            }
        }
        return amountToAdd;
    }

    /**
     * 计算RSI（相对强弱指数）并设置到ETFMedianInfo对象中。
     * RSI用于衡量一段时间内价格上涨与下跌的强度，常用于判断超买超卖。
     *
     * @param etfMedianInfo 结果对象，将计算出的RSI写入其中
     * @param list          ETF历史数据列表（通常为连续的若干天）
     */
    private void countRSI(ETFMedianInfo etfMedianInfo, List<ETFData> list) {
        try {
            BigDecimal rsi = countWilderRSI(list);
            etfMedianInfo.setRsi(rsi);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 创建包含volume的ETFData
     * 优先从SoHuETFUtil获取包含volume的数据，如果失败则使用ETF对象创建
     *
     * @param etf ETF对象
     * @return ETFData对象，尽可能包含volume数据
     */
    private ETFData createETFDataWithVolume(ETF etf) {
        try {
            // 尝试从SoHuETFUtil获取包含volume的数据
            SoHuETFUtil.SoHuETF soHuETF = SoHuETFUtil.getETF(etf.getCode());
            if (soHuETF != null && soHuETF.getVolume() != null && soHuETF.getVolume() > 0) {
                // 创建包含volume的ETFData
                ETFData etfData = new ETFData(etf);
                etfData.setVolume(soHuETF.getVolume());
                log.debug("从SoHuETFUtil获取成交量成功：code={}, volume={}", etf.getCode(), soHuETF.getVolume());
                return etfData;
            }
        } catch (Exception e) {
            log.debug("从SoHuETFUtil获取成交量失败，使用ETF对象创建：code={}, error={}", etf.getCode(), e.getMessage());
        }
        // 如果获取失败，使用ETF对象创建（volume为null）
        return new ETFData(etf);
    }

    /**
     * 计算当日量比
     * 量比 = 当日成交量 / 时间范围内的平均成交量
     * 注意：调用此方法时，list已经被反转，第一个元素（index 0）是当日数据
     *
     * @param etfMedianInfo 结果对象，将计算出的量比写入其中
     * @param list          ETF历史数据列表（已反转，第一个元素是当日数据）
     * @param etfData       当日ETF数据
     */
    private void calculateVolumeRatio(ETFMedianInfo etfMedianInfo, List<ETFData> list, ETFData etfData) {
        try {
            // 获取当日成交量
            Long todayVolume = etfData.getVolume();
            if (todayVolume == null || todayVolume == 0) {
                log.debug("计算量比失败：当日成交量为空或为0，code={}", etfData.getCode());
                etfMedianInfo.setVolumeRatio(null);
                return;
            }

            // 计算历史数据的平均成交量（排除当日数据）
            // list已经被反转，第一个元素（index 0）是当日数据，从index 1开始是历史数据
            if (list.size() <= 1) {
                log.debug("计算量比失败：历史数据不足，code={}", etfData.getCode());
                etfMedianInfo.setVolumeRatio(null);
                return;
            }

            // 计算平均成交量（从index 1开始，排除当日数据）
            long totalVolume = 0;
            int count = 0;
            for (int i = 1; i < list.size(); i++) {
                ETFData data = list.get(i);
                Long volume = data.getVolume();
                if (volume != null && volume > 0) {
                    totalVolume += volume;
                    count++;
                }
            }

            if (count == 0) {
                log.debug("计算量比失败：历史数据中没有有效的成交量数据，code={}, 历史数据条数={}", 
                    etfData.getCode(), list.size() - 1);
                etfMedianInfo.setVolumeRatio(null);
                return;
            }

            BigDecimal avgVolume = BigDecimal.valueOf(totalVolume)
                .divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP);

            if (avgVolume.compareTo(BigDecimal.ZERO) == 0) {
                log.debug("计算量比失败：平均成交量为0，code={}", etfData.getCode());
                etfMedianInfo.setVolumeRatio(null);
                return;
            }

            // 计算量比
            BigDecimal volumeRatio = BigDecimal.valueOf(todayVolume)
                .divide(avgVolume, 2, RoundingMode.HALF_UP);
            etfMedianInfo.setVolumeRatio(volumeRatio);
            log.debug("计算量比成功：code={}, 当日成交量={}, 平均成交量={}, 量比={}", 
                etfData.getCode(), todayVolume, avgVolume, volumeRatio);
        } catch (Exception e) {
            log.error("计算量比时发生错误: code={}, error={}", etfData.getCode(), e.getMessage(), e);
            etfMedianInfo.setVolumeRatio(null);
        }
    }

    public static void main(String[] args) {
        long between = DAYS.between(LocalDate.now(),
            LocalDate.now().plusDays(1));
        System.out.println(between);
    }

    //获取最近涨跌幅度
    private static double getRate(List<ETFData> list) {
        double baseValue = 1;
        boolean first = true;
        for (ETFData data : list) {
            double dailyRate = data.getDailyRate()
                .doubleValue() / 100;
            if (first) {
                baseValue = baseValue * (1 + dailyRate);
                first = false;
            } else {
                if (baseValue >= 1 && dailyRate >= 0) {
                    //上涨
                    baseValue = baseValue * (1 + dailyRate);
                } else if (baseValue <= 1 && dailyRate <= 0) {
                    //下跌
                    baseValue = baseValue * (1 + dailyRate);
                } else {
                    break;
                }
            }
        }
        return (baseValue - 1) * 100;
    }

    private List<ETFData> clearList(List<ETFData> list) {
        List<ETFData> res = new ArrayList<>();
        for (ETFData etfData : list) {
            res.add(etfData);
            double v = etfData.getDailyRate().doubleValue();
            if (v < -20 || v > 20) {
                res = new ArrayList<>();
            }
        }
        return res;
    }

    /**
     * 初始化并计算每一天的辅助累计净值（avgValue）。
     * avgValue 表示：假设初始净值为 base（如100），按每天的 totalRate（复权因子）连续复利计算到当前日，得到的理论净值。
     * 该值用于后续的涨跌幅、最大/最小/区间等统计分析。
     *
     * @param list ETF历史数据列表，需按时间顺序排列（早到晚）
     * @param base 初始基准值，通常为100
     */
    public void setAveValue(List<ETFData> list, BigDecimal base) {
        BigDecimal cValueDate = null;
        for (ETFData e : list) {
            if (cValueDate == null) {
                // 第一天，累计净值直接等于基准值
                cValueDate = base;
                e.setAvgValue(cValueDate);
            } else {
                // 从第二天起，累计净值 = 前一天累计净值 × 当天复权因子
                cValueDate = cValueDate.multiply(e.getTotalRate());
                e.setAvgValue(cValueDate);
            }
        }
    }

    //检查历史数据完整
    public String checkDate(Set<String> codeSet, List<LocalDate> dates) {
        //检查历史数据完整
        //throw new RuntimeException("历史数据未更新，请检查");
        return codeSet.stream().filter(code -> {
            //检查历史数据完整
            boolean b = dates.stream().anyMatch(date -> checkDate(code, date));
            if (!b) {
                log.info("检查上一交易日无数据：{} - {}", code, dates);
            }
            return !b;
        }).findFirst().orElse(null);
    }

    public String checkDateFail(Set<String> codeSet, List<LocalDate> dates) {
        return checkDate(codeSet, dates);
    }

    //检查历史数据完整
    public boolean checkDate(String code, LocalDate date) {
        //根据code和date查询历史数据是否存在
        return ietfDataService.existByCodeAndDate(code, date);
    }

    // 更新连续3天下跌概率
    public void update3DownRate(String code) {
        List<ETFData> etfData = ietfDataService.listByCode(code);
        List<Integer> list = new ArrayList<>();
        int temp = 0;
        for (ETFData e : etfData) {
            if (e.getDailyRate().compareTo(BigDecimal.ZERO) < 0) {
                temp++;
            } else {
                if (temp >= 2) {
                    list.add(temp);
                }
                temp = 0;
            }
        }

        long count = list.stream()
            .filter(e -> e >= 4)
            .count();
        BigDecimal rate = BigDecimal.valueOf(count).divide(BigDecimal.valueOf(list.size()), 4, RoundingMode.HALF_UP);
        ietfService.update3DownRate(code, rate);
    }
}
